//TODO: PixelDivision, test

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// PixelDivision is an algorithm to divide two images
    /// </summary>
    public class PixelDivision : BaseImageFilter
    {
        //the width of the input images in pixels
        private int i1_w;
        private int i2_w;

        //the width and height of the output image

        private int d_w;
        private int d_h;
        private int[] dest_1d;
        private bool is_colored;
        private bool m_bDivide = true;
        private float m_fConstant = 0.0f;

        //TODO: check if this can be deleted (inheritance)
        private float m_fOffset = 0.0f;
        //TODO: check if this can be deleted (inheritance)
        private float m_fScale = 1.0f;
        private bool m_bReverse = false;
        private double[,] m_aSum = null;

        /// <summary>
        /// Constructs a new PixelDivision
        /// firstwidth The width of the first image
        /// secondwidth The width of the second image
        ///
        /// </summary>
        public PixelDivision()
            : this(true, 0.0f, 0.0f, 1.0f, false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PixelDivision"/> class.
        /// </summary>
        /// <param name="sum">The sum.</param>
        public PixelDivision(double[,] sum)
        {
            m_aSum = sum;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PixelDivision"/> class.
        /// </summary>
        /// <param name="_bDivide">if set to <c>true</c> [_b divide].</param>
        /// <param name="_fConstant">The _f constant.</param>
        /// <param name="_fOffset">The _f offset.</param>
        /// <param name="_fScale">The _f scale.</param>
        /// <param name="_bReverse">if set to <c>true</c> [_b reverse].</param>
        public PixelDivision(bool _bDivide, float _fConstant, float _fOffset, float _fScale, bool _bReverse)
        {
            m_bDivide = _bDivide;
            m_bDivide = _bDivide;
            m_fConstant = _fConstant;
            m_fOffset = _fOffset;
            m_fScale = _fScale;
            m_bReverse = _bReverse;
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            if (m_aSum == null)
            {
                if (m_bDivide)
                {
                    ApplyDivision(_bitmap, m_fConstant, m_fOffset, m_fScale, m_bReverse);
                }
                else
                {
                    //ApplyDivision(_bitmap, bitmap2, 0, 0, m_fOffset, m_fScale, m_bReverse);
                }
            }
            else
            {
                DivBitmap(_bitmap, m_aSum);
            }
            return _bitmap;
        }

        /// <summary>
        /// Divs the bitmap.
        /// </summary>
        /// <param name="img">The img.</param>
        /// <param name="divider">The divider.</param>
        /// <returns></returns>
        private double[,] DivBitmap(UnsafeBitmap img, double[,] divider)
        {
            UnsafeBitmap bitmap = img;
            int nfil = divider.GetLength(0);
            int ncol = divider.GetLength(1);
            double[,] result = new double[nfil, ncol];
            Bitmap = img;
            for (int y = 0; y < ncol; y++)
            {
                for (int x = 0; x < nfil; x++)
                {
                    //result[x, y] += (double)pPixel->gray / (divider[x, y] + 1);
                }
            }
            return result;
        }

        /// <summary>
        ///
        /// Applies the image Div operator on the specified image arrays, with the
        /// specified offset and scale value
        /// image product algorithm..
        /// a) assume the image is grey level (hence RR=GG=BB)
        ///  b) use value <!-- &0x000000ff to get the BB value -->
        ///  c) do this for both input images
        ///  d) apply the operation (eg Div)
        ///  e) add 128 and then scale
        ///  f) clip to lie from 0 to 255. Call this value 0xCD
        ///  g) create int value 0xffCDCDCD
        /// </summary>
        /// <param name="_bitmap1">The first source image as a pixel array</param>
        /// <param name="_bitmap2">The second source image as a pixel array</param>
        /// <param name="width">width of the destination image in pixels</param>
        /// <param name="height">height of the destination image in pixels</param>
        /// <param name="oset">The offset value</param>
        /// <param name="scale">The scale value</param>
        /// <param name="reverse">Reverse the order of the division</param>
        public void ApplyDivision(int[] _bitmap1, int[] _bitmap2, int width, int height,
                    float oset, float scale, bool reverse)
        {

            int place1 = -1;
            int place2 = -1;
            int src1rgb = 0;
            int src2rgb = 0;
            int result = 0;
            //Get size of image and make 1d_arrays
            d_w = width;
            d_h = height;


            dest_1d = new int[d_w * d_h];

            bool firstwider = false;
            bool secondwider = false;
            int wrap;

            if (i1_w > d_w)
            {
                wrap = ((i1_w + 1) - d_w);
                firstwider = true;
            }
            else if (i2_w > d_w)
            {
                wrap = ((i2_w + 1) - d_w);
                secondwider = true;

            }
            else
            {
                wrap = 0;
            }

            //if you know there is no wrap around, you can save yourself some time
            if (wrap == 0)
            {
                for (int i = 0; i < dest_1d.Length; i++)
                {
                    src2rgb = _bitmap2[i] & 0x000000ff;
                    src1rgb = _bitmap1[i] & 0x000000ff;
                    if (!reverse)
                    {
                        result = (int)((scale * ((float)src2rgb / (float)src1rgb)) + oset);
                    }
                    else
                    {
                        result = (int)((scale * ((float)src1rgb / (float)src2rgb)) + oset);
                    }
                    //clip to 0 ... 256
                    if (result < 0)
                    {
                        result = 0;
                    }
                    else if (result > 255)
                    {
                        result = 255;
                    }

                    //create an int value for dest_1d
                    dest_1d[i] = (int)(0xFF << 24 | (result + (result << 16) + (result << 8)));

                }
            }
            else
            { // wrap != 0

                for (int i = 0; i < dest_1d.Length; i++)
                {
                    //we might need to skip out some pixels which aren't in the overlap area
                    if ((i % d_w) == 0)
                    {
                        if (i == 0)
                        {
                            place1 = 0;
                            place2 = 0;
                        }
                        else if (secondwider)
                        {
                            place2 = place2 + wrap;
                            place1++;
                        }
                        else
                        {
                            place1 = place1 + wrap;
                            place2++;
                        }
                    }
                    else
                    {
                        place2++;
                        place1++;
                    }

                    src2rgb = _bitmap2[place2] & 0x000000ff;
                    src1rgb = _bitmap1[place1] & 0x000000ff;

                    if (!reverse)
                    {
                        if (src1rgb == 0) src1rgb = 1;
                        result = (int)((scale * ((float)src2rgb / (float)src1rgb)) + oset);
                    }
                    else
                    {
                        if (src2rgb == 0) src2rgb = 1;
                        result = (int)((scale * ((float)src1rgb / (float)src2rgb)) + oset);
                    }

                    //clip to 0 ... 256
                    if (result < 0)
                    {
                        result = 0;
                    }
                    else if (result > 255)
                    {
                        result = 255;
                    }

                    //create an int value for dest_1d
                    dest_1d[i] = (int)(0xFF << 24 | (result + (result << 16) + (result << 8)));
                }
            }
        }

        /// <summary>
        /// Divides the specified input image by the constant, also adds offset
        /// no need for scaling in this example.
        /// </summary>
        /// <param name="_bitmap">The input pixel array</param>
        /// <param name="fConstant">The constant value to divide every pixel in the input array by</param>
        /// <param name="oset">The offset value</param>
        /// <param name="scale">The scale value</param>
        /// <param name="reverse"></param>
        public void ApplyDivision(UnsafeBitmap _bitmap, float fConstant, 
                    float oset, float scale, bool reverse)
        {
            int nSrc1rgb;
            int nResult = 0;

            UnsafeBitmap bitmap = _bitmap;
            Color color = Color.Transparent;
            int nValue = 0;
            int nColorResult = 0;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            //now do the division on the input image 
            if (reverse)
            {
                if (fConstant == 0)
                {
                    //TODO: PIxelDivision
                    //showMessageDialog(null, ("Constant can't be 0"), ("Error!"), WARNING_MESSAGE);
                    fConstant = 1;
                }
                else
                {
                    for (int y = 0; y < nHeight; y++)
                    {
                        for (int x = 0; x < nWidth; x++)
                        {
                            color = bitmap.GetPixel(x, y);
                            nValue = color.ToArgb();
                            nSrc1rgb = nValue & 0x000000ff;
                            nResult = (int)(scale * ((float)nSrc1rgb / fConstant) + oset);
                            if (nResult > 255)
                            {
                                nResult = 255;
                            }
                            if (nResult < 0)
                            {
                                nResult = 0;
                            }
                            nColorResult = (int)(0xFF << 24 | (nResult + (nResult << 16) + (nResult << 8)));
                            bitmap.SetPixel(x,y,Color.FromArgb(nColorResult));
                        }
                    }
                }
            }
            else
            {
                for (int y = 0; y < nHeight; y++)
                {
                    for (int x = 0; x < nWidth; x++)
                    {
                        color = bitmap.GetPixel(x, y);
                        nValue = color.ToArgb();
                        nSrc1rgb = nValue & 0x000000ff;
                        nResult = (int)(scale * (fConstant / (float)nSrc1rgb) + oset);
                        if (nResult > 255)
                        {
                            nResult = 255;
                        }
                        if (nResult < 0)
                        {
                            nResult = 0;
                        }
                        nColorResult = (int)(0xFF << 24 | (nResult + (nResult << 16) + (nResult << 8)));
                        bitmap.SetPixel(x,y,Color.FromArgb(nColorResult));
                    }
                }
            }
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new PixelDivision(this.m_bDivide, this.m_fConstant, this.m_fOffset, this.m_fScale, this.m_bReverse);
        }
    }
}